package com.cssw.fyzb.busi.service.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cssw.fyzb.busi.cache.Cache;
import com.cssw.fyzb.busi.cache.CacheKeys;
import com.cssw.fyzb.busi.config.TpsClient;
import com.cssw.fyzb.busi.util.BizUtil;
import com.cssw.fyzb.busi.util.Const;
import com.cssw.fyzb.busi.util.UuidUtil;
import com.cssw.fyzb.core.dao.acct.AcctDao;
import com.cssw.fyzb.core.dao.acct.RnbExchangeCoinLogDao;
import com.cssw.fyzb.core.dao.pay.PayNotifyDao;
import com.cssw.fyzb.core.dao.pay.PayOrderDao;
import com.cssw.fyzb.core.dao.pay.UserPayDao;
import com.cssw.fyzb.core.dao.system.SysParamDao;
import com.cssw.fyzb.core.dao.tesk.RuleInstDao;
import com.cssw.fyzb.core.dao.tesk.TaskDao;
import com.cssw.fyzb.core.dao.user.UserDao;
import com.cssw.fyzb.core.eao.acct.AcctLogEao;
import com.cssw.fyzb.core.exception.BizException;
import com.cssw.fyzb.core.mao.task.TaskStatMao;
import com.cssw.fyzb.core.mao.user.UserMao;
import com.cssw.fyzb.core.model.dodb.*;
import com.cssw.fyzb.core.model.mos.TaskStatMo;
import com.cssw.fyzb.core.model.vo.task.CollecTaskVo;
import com.cssw.fyzb.core.util.AesUtil;
import com.cssw.fyzb.core.util.StringUtil;
import com.cssw.fyzb.core.util.TimeUtil;
import com.ydn.appserver.Request;
import com.ydn.appserver.Response;
import com.ydn.dbframe.plugin.activerecord.Db;
import com.ydn.simplelock.SimpleLock;
import com.ydn.simplelock.SimpleLockFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * @author zhouxu
 * @Date 20201218
 */
@Service
public class PayServiceImpl implements PayService {

    private static final Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);

    @Autowired
    private TpsClient tpsClient;
    @Autowired
    private UserPayDao userPayDao;
    @Autowired
    private PayOrderDao payOrderDao;
    @Autowired
    private AcctDao acctDao;
    @Autowired
    private AcctLogEao acctLogEao;
    @Autowired
    private SimpleLockFactory simpleLockFactory;
    @Autowired
    private UserDao userDao;
    @Autowired
    private UserMao userMao;

    @Value("${task.limitDate:2021-01-21 00:00:00}")
    private String limitDateStr;

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private RuleInstDao ruleInstDao;

    @Autowired
    private TaskStatMao taskStatMao;

    @Autowired
    private RnbExchangeCoinLogDao rnbExchangeCoinLogDao;

    @Autowired
    private PayNotifyDao payNotifyDao;

    @Autowired
    private SysParamDao sysParamDao;

    @Override
    public PayOrder createPayOrder(Long userId, Integer payAmount, String payBusi, String outTradeNo, String payWay, BigDecimal fCoinNum) {
        return payOrderDao.createOrder(userId, payAmount, payBusi, outTradeNo, payWay, fCoinNum);
    }

    @Override
    public PayOrder findByUserPayAuthOrder(Long userId) {
        String sql = "select * from t_pay_order where user_id = ? and pay_busi = ? ";
        return PayOrder.dao.findFirst(sql, userId, PayOrder.PAY_USER_AUTH);
    }

    @Override
    public PayOrder findByUserVipOrder(Long userId) {
        String sql = "select * from t_pay_order where user_id = ? and pay_busi = ? ";
        return PayOrder.dao.findFirst(sql, userId, PayOrder.PAY_USER_VIP);
    }

    @Override
    public void updatePayOrderInit(PayOrder order, String outTradeNo) {
        payOrderDao.updateStatusOrder(order, PayOrder.STATUS_INIT, outTradeNo);
    }

    @Override
    public Response aliPay(PayOrder order) {

        Request request = new Request("AliAppPay");
        request.setParameter("body", Const.PAY_BODY);
        request.setParameter("subject", Const.PAY_SUBJECT);
        request.setParameter("outTradeNo", order.getOutTradeNo());
        request.setParameter("amount", order.getPayAmount());
        // 支付中
        payOrderDao.updateStatus(order, PayOrder.STATUS_PAYING);
        Response response = tpsClient.send(request);
        if (response.isSuccess()) {
            // 已支付
            payOrderDao.updateStatus(order, PayOrder.STATUS_PAY_SEND);
            // 第三方流水记录
            userPayDao.updateUserPay(order.getUserId(), order.getOutTradeNo(), UserPayLog.PAY_TYPE_ALI.toString());
        }

        return response;
    }

    @Override
    public Response aliAdapay(PayOrder order, String goodsTitle, String goodsDesc) {
        Request request = new Request("AliAppAdapay");
        request.setParameter("goodsTitle", goodsTitle);
        request.setParameter("goodsDesc", goodsDesc);
        request.setParameter("outTradeNo", order.getOutTradeNo());
        request.setParameter("payAmt", new BigDecimal(order.getPayAmount()).divide(new BigDecimal(100)).setScale(2));

        // 支付中
        payOrderDao.updateStatus(order, PayOrder.STATUS_PAYING);
        Response response = tpsClient.send(request);
        if (response.isSuccess()) {
            // 已支付
            payOrderDao.updateStatus(order, PayOrder.STATUS_PAY_SEND);
            // 第三方流水记录
            userPayDao.updateUserPay(order.getUserId(), order.getOutTradeNo(), UserPayLog.PAY_TYPE_ALI.toString());
        }

        return response;
    }

    @Override
    public Response wxPay(PayOrder order) {
        Request request = new Request("WxAppPay");
        request.setParameter("body", Const.PAY_BODY);
        request.setParameter("subject", Const.PAY_SUBJECT);
        request.setParameter("outTradeNo", order.getOutTradeNo());
        request.setParameter("amount", order.getPayAmount());
        // 支付中
        payOrderDao.updateStatus(order, PayOrder.STATUS_PAYING);
        Response response = tpsClient.send(request);
        if (response.isSuccess()) {
            // 已支付
            payOrderDao.updateStatus(order, PayOrder.STATUS_PAY_SEND);
            // 第三方流水记录
            userPayDao.updateUserPay(order.getUserId(), order.getOutTradeNo(), UserPayLog.PAY_TYPE_WECHAT.toString());
        }

        return response;

    }

    @Override
    public void updatePayStatus(PayOrder order, String status) {
        payOrderDao.updateStatus(order, status);
    }

    @Override
    public void updateUserPay(Long userId, String tradeNo, String payType) {
        userPayDao.updateUserPay(userId, tradeNo, payType);
    }

    @Override
    public Response wxAdapay(PayOrder order, String goodsTitle, String goodsDesc, String expend) {
        if (StringUtil.isEmpty(expend)) {
            return Response.fail("必传参数缺失");
        }

        Request request = new Request("WxAppAdapay");
        request.setParameter("goodsTitle", goodsTitle);
        request.setParameter("goodsDesc", goodsDesc);
        request.setParameter("expend", expend);
        request.setParameter("outTradeNo", order.getOutTradeNo());
        request.setParameter("payAmt", order.getPayAmount());
        // 支付中
        payOrderDao.updateStatus(order, PayOrder.STATUS_PAYING);
        Response response = tpsClient.send(request);
        if (response.isSuccess()) {
            // 已支付
            payOrderDao.updateStatus(order, PayOrder.STATUS_PAY_SEND);
            // 第三方流水记录
            userPayDao.updateUserPay(order.getUserId(), order.getOutTradeNo(), UserPayLog.PAY_TYPE_WECHAT.toString());
        }

        return response;
    }

    @Override
    public boolean queryAdapay(String tradeNo) {
        return false;
    }

    @Override
    public boolean wxOrderQuery(String tradeNo) {
        Request request = new Request("WxOrderQuery");
        request.setParameter("tradeNo", tradeNo);
        Response response = tpsClient.send(request);

        if (response.getCode() == 500) {
            throw new BizException(response.getCode(), "查询异常，请重试");
        }
        return response.isSuccess();
    }

    @Override
    public boolean aliOrderQuery(String tradeNo) {
        Request request = new Request("AliOrderQuery");
        request.setParameter("tradeNo", tradeNo);
        Response response = tpsClient.send(request);

        if (response.getCode() == 500) {
            throw new BizException(response.getCode(), "查询异常，请重试");
        }

        if (!response.isSuccess()) {
            return false;
        }

        //tradeStatus 交易状态：WAIT_BUYER_PAY（交易创建，等待买家付款）、TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）、TRADE_SUCCESS（交易支付成功）、TRADE_FINISHED（交易结束，不可退款）

        Map map = response.getData().get("data", Map.class);
        if (map != null && "TRADE_SUCCESS".equals(map.get("tradeStatus").toString())) {
            return true;
        }

        return false;
    }

    @Override
    public void afterPayBusi(String tradeNo) {
        // 订单校验
        PayOrder order = payOrderDao.findByTradeNo(tradeNo);
        if (order == null || !order.isPaySend()) {
            BizUtil.exception("订单异常：tradeNo=" + tradeNo);
        }
        // 获取锁
        SimpleLock lock = simpleLockFactory.getLock("USER_" + order.getUserId());
        try {
            lock.acquire();
            // 订单，账本变动
            handleOrderAcctBusi(order);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                lock.release();
            } catch (Exception e) {
                throw e;
            }
        }

    }

    @Override
    public void saveAdapayNotifyLog(String params) {
        try{
            JSONObject jsonObject = JSON.parseObject(params);
            String originalStr = params;
            if (originalStr.length() > 1800){
                originalStr = originalStr.substring(0, 1800);
            }
            payNotifyDao.saveAdapay(jsonObject.getString("order_no"), jsonObject.getString("id"),
                    jsonObject.getString("created_time"), jsonObject.getString("app_id"),
                    jsonObject.getString("expend"), jsonObject.getString("end_time"),
                    jsonObject.getString("pay_channel"), jsonObject.getString("status"),
                    jsonObject.getString("pay_amt"), originalStr);
        } catch (Exception e){
            logger.error("", e);
        }
    }

    @Override
    public PayOrder getByTradeNo(String tradeNo) {
        return payOrderDao.findByTradeNo(tradeNo);
    }

    private void handleOrderAcctBusi(PayOrder order) {
        // 订单状态修改--已完成
        payOrderDao.updateStatus(order, PayOrder.STATUS_FINISHED);
        // 分业务场景处理
        switch (order.getPayBusi()) {
            case PayOrder.PAY_BUSI_BUY_F_COIN:// 1.福币购买
                Acct acct = acctDao.queryFAcct(order.getUserId());
                // 加福币
                acctDao.addFBalance(order.getUserId(), order.getFCoinNum(), order.getOrderId().toString(), AcctTradeType.BUY_F_COIN, BusiType.BUY_F_COIN);

                //记录es日志
                acctLogEao.save(UuidUtil.get(), acct.getAcctId(), order.getUserId(), AcctType.ACCT_TYPE_F, order.getFCoinNum(),
                        Acct.FEE_IN_DE_ADD, order.getOrderId().toString(), AcctTradeType.BUY_F_COIN, AcctTradeType.BUY_F_COIN_NAME,
                        BusiType.BUY_F_COIN, new Date());
                updateBuyCoinStatus(order.getOutTradeNo(), RnbExchangeCoinLog.STATUS_SUCC, "");
                break;

            case PayOrder.PAY_USER_AUTH:// 实名认证支付
                userDao.updateAuthPay(order.getUserId());
                User user = userDao.getById(order.getUserId());
                if (user.isFace()) {
                    userDao.updateFaceAuth(user, User.FACE_AUTH_YES);
                    userDao.saveCmfUserAuth(user.getUserName(), "实名成功", true, user.getUserId().intValue());
                    userMao.updateFaceAuth(user.getUserId(), User.FACE_AUTH_YES);
                    //实名成功，赠送任务
                    giftTask(user.getUserId(), Long.parseLong(RuleInst.GIVE_TASK));

                    Cache.remove(CacheKeys.mobileKey(user.getMobile()));
                }
                break;
            case PayOrder.PAY_USER_VIP:// 开通vip会员支付
                String userOpenVipTimeLong = sysParamDao.findByCode(SysParam.USER_OPEN_VIP_TIME_LONG).getParamValue();
                userDao.updateVip(order.getUserId(), User.VIP_YES, userOpenVipTimeLong);
                userMao.updateVip(order.getUserId(), User.VIP_YES);
                User vipUser = userDao.getById(order.getUserId());
                Cache.remove(CacheKeys.mobileKey(vipUser.getMobile()));
                break;
            case PayOrder.PAY_BUSI_BUY_GZ_COIN:// 1.果子购买
                Acct gzAcct = acctDao.queryGAcct(order.getUserId());
                // 加果子
                acctDao.addGBalance(order.getUserId(), order.getFCoinNum(), order.getOrderId().toString(), AcctTradeType.BUY_F_COIN, BusiType.BUY_F_COIN);

                //记录es日志
                acctLogEao.save(UuidUtil.get(), gzAcct.getAcctId(), order.getUserId(), AcctType.ACCT_TYPE_G, order.getFCoinNum(),
                        Acct.FEE_IN_DE_ADD, order.getOrderId().toString(), AcctTradeType.BUY_GZ_COIN, AcctTradeType.BUY_GZ_COIN_NAME,
                        BusiType.BUY_GZ_COIN, new Date());

                updateBuyCoinStatus(order.getOutTradeNo(), RnbExchangeCoinLog.STATUS_SUCC, "");
                break;

            default:
                BizUtil.exception("订单异常：payBusi=" + order.getPayBusi());
                break;
        }
    }

    private void updateBuyCoinStatus(String outTradeNo, String status, String remark) {
        //只更新日志，抛异常
        try{
            RnbExchangeCoinLog rnbExchangeCoinLog = rnbExchangeCoinLogDao.getByTradeNo(outTradeNo);
            if (null == rnbExchangeCoinLog){
                return;
            }
            String newRemark = rnbExchangeCoinLog.getRemark() + remark;
            rnbExchangeCoinLogDao.updateStatus(rnbExchangeCoinLog.getLogId(), status, newRemark);
        } catch (Exception e){
            logger.error(">>>" + outTradeNo, e);
        }
    }

    private void giftTask(Long userId, Long taskId) {
        //判断该用户是否已获取任务
        User user = userDao.getById(userId);

        //注册老用户不能领取任务
        Date limitDate = TimeUtil.parse(this.limitDateStr, "yyyy-MM-dd HH:mm:ss");
        if (null != limitDate && null != user.getCreateTime() && user.getCreateTime().before(limitDate)) {
            return;
        }

        Integer rule = ruleInstDao.getRuleInst(userId, taskId);
        Task task = taskDao.getTask(taskId);
        if (task == null) {
            return;
        }
        //不需要用果子兑换的就不用校验密码
        if (task.getPrice().compareTo(new BigDecimal(0)) == 1) {
            if (StringUtil.isEmpty(user.getTradePwd())) {
                return;
            }
        }
        if (!user.getFaceAuth().equals("1")) {
            return;
        }
        if (task.getLimitNum() <= rule) {
            return;
        }
        //判断用户果币是否足够
        Acct acct = acctDao.queryAcctByUIdAndType(userId, AcctType.ACCT_TYPE_G);
        if (acct == null) {
            return;
        }
        if (StringUtil.isNotEmpty(task.getPrice().toString())) {
            if ((task.getPrice().compareTo(acct.getBalanceFee())) == 1) {//price 大于 balanceFee 苹果数大于账户余额
                return;
            }
        }
        Db.tx(() -> {
            Boolean isTask = false; //是否是今日任务
            RuleInst ruleInst = new RuleInst();
            ruleInst.setUserId(userId);
            ruleInst.setTaskId(task.getTaskId());
            ruleInst.setCreateTime(new Date());
            //判断用户今天是否有任务,有任务生效时间推迟到第二天零点
            RuleInst userTask = ruleInstDao.getDayTask(userId);
            Calendar ca = Calendar.getInstance();
            if (userTask != null) {
                //获取明天零点时间
                ca.add(Calendar.DATE, 1);
                Date time = ca.getTime();
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd 00:00:00");//注意月份是MM
                try {
                    ruleInst.setEffDate(simpleDateFormat.parse(simpleDateFormat.format(time)));
                    ca.setTime(time);
                    ca.add(Calendar.DATE, task.getProduceDay() + 1);
                    ruleInst.setExpDate(simpleDateFormat.parse(simpleDateFormat.format(ca.getTime())));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            } else {
                //没有任务拿当前时间
                ruleInst.setEffDate(ruleInst.getCreateTime());
                //结束时间
                ca.setTime(ruleInst.getCreateTime());
                ca.add(Calendar.DATE, task.getProduceDay());
                ruleInst.setExpDate(ca.getTime());
                isTask = true;

            }
            ruleInst.setAllValue(task.getAllValue());
            //剩余产值
            ruleInst.setSurplusValue(task.getAllValue());

            ruleInst.setStatus(ruleInst.STATUS_ONGOING);
            ruleInst.setAllDay(0);
            //每日释放值
            //天数转BigDecimal
            BigDecimal b = new BigDecimal(task.getProduceDay());
            BigDecimal divide = task.getAllValue().divide(b, 3, BigDecimal.ROUND_DOWN);
            //每天完成任务的平安果
            ruleInst.setDayValue(divide);
            Long save = ruleInstDao.save(ruleInst);
            if (isTask) {
                //清空mongo(第一个任务之前今天的广告不算)
                TaskStatMo taskStatMo = taskStatMao.findByUserId(userId);
                if (taskStatMo != null) {
                    taskStatMo.setViewDayTime(0);
                    taskStatMo.setInstId(save);
                    taskStatMo.setTaskTime(task.getTaskTime().longValue());
                    //设置状态为有任务
                    taskStatMo.setDayTaskFlag("1");
                    taskStatMao.update(taskStatMo);
                }
            }
            //任务添加成功 账户扣掉任务果币
            if (task.getPrice().compareTo(new BigDecimal(0)) == 1) {
                //值存入用户账号
                acctDao.minusBalance(userId, "G", task.getPrice(), save.toString(),
                        AcctTradeType.EXCHANGE_REDUCE, BusiType.EXCHANGE_CODE);

                acctLogEao.save(UuidUtil.get(), acct.getAcctId(), userId, AcctType.ACCT_TYPE_G, task.getPrice(),
                        Acct.FEE_IN_DE_MINUS, save.toString(), AcctTradeType.EXCHANGE_REDUCE, AcctTradeType.EXCHANGE_REDUCE_NAME,
                        BusiType.EXCHANGE_CODE, new Date());
            }

            return true;
        });
    }


}
